#pragma once
#include "Socket.hpp"
#include "Epoller.hpp"
#include <memory>
#include <string>
#include <unordered_map>
#include "log.hpp"
#include <functional>
#define DEFAULT_NUM 128

LOG lg(PRINT_TO_SCREEN);

class Connection;
class TcpServer;

using func_t = std::function<void(std::weak_ptr<Connection>)>;//回调函数类型
using except_func = std::function<void(std::weak_ptr<Connection>)>;

int EVENT_IN = (EPOLLIN|EPOLLET);
int EVENT_OUT = (EPOLLOUT|EPOLLET);

class Connection
{
public:
    Connection(int fd, const std::string &ip, int port)
        : _fd(fd), _ip(ip), _port(port)
    {
        
    }
    

    void SetHandler(func_t recv_cb, func_t send_cb, except_func except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    std::string& Inbuffer()
    {
        return _read_buf;
    }
    std::string& Outbuffer()
    {
        return _write_buf;
    }

    void AppendInbuffer(const std::string &str)
    {
        _read_buf += str;
    }
    void AppendOutbuffer(const std::string &str)
    {
        _write_buf += str;
    }

    int SockFd() const
    {
        return _fd;
    }
    std::string getIp() const
    {
        return _ip;
    }
    int getPort() const
    {
        return _port;
    }

    std::weak_ptr<TcpServer> getServer()
    {
        return _server;
    }

    void setServer(std::shared_ptr<TcpServer> server)
    {
        _server = server;
    }

    ~Connection()
    {
    }
private:

    int _port;
    std::string _ip;
    int _fd;

    std::weak_ptr<TcpServer> _server;//设置回调指针

    //还需要设置两个缓冲区
    std::string _read_buf;
    std::string _write_buf;
public:
    //设置回调函数
    func_t _recv_cb;
    func_t _send_cb;
    except_func _except_cb;
};

class TcpServer:public std::enable_shared_from_this<TcpServer>//继承声明表示TcpServer类需要安全地获取自身的shared_ptr，并且可以延长生命周期，确保在调用时没有销毁
{
public:
    TcpServer(int port,func_t cb)
        : _port(port)
        ,_Onmessage(cb)
        ,_listen_sock(std::make_shared<Sockets>())//mark_shared就是在创建一个对象
        ,_epoller(std::make_shared<Epoller>())
    {
    }

    void SetNonBlock(int fd)
    {
        int flag = fcntl(fd,F_GETFL,0);
        if(flag == -1)
        {
            lg(ERROR,"fcntl error");
            exit(1);
        }
        fcntl(fd,F_SETFL,flag | O_NONBLOCK);
    }

    void Init()
    {
        _listen_sock->Socket();
        SetNonBlock(_listen_sock->Getsocketfd());
        _listen_sock->Bind(_port);
        _listen_sock->Listen();
        AddConnection(_listen_sock->Getsocketfd(),EVENT_IN,std::bind(&TcpServer::Accepter,this,std::placeholders::_1), [](auto&&){}, [](auto&&){});
        //注意这里不能直接传空指针，需要传一个lambda表达式
    }


    void AddConnection(int fd,uint32_t events,func_t recv_cb,func_t send_cb,except_func except_cb,const string& ip="0.0.0.0",int port=0)
    {
        std::shared_ptr<Connection> conn = std::make_shared<Connection>(fd,ip,port);
        conn->setServer(shared_from_this());//shared_from_this()获取指向当前对象的指针
        conn->SetHandler(recv_cb,send_cb,except_cb);
        _conn_map[fd] = conn;//将信息添加到哈希表中
        _epoller->EpollUpdate(EPOLL_CTL_ADD,fd,events);
        lg(DEBUG, "add a new connection success, sockfd is : %d", fd);
    }

    void Accepter(std::weak_ptr<Connection> conn)
    {
        if(conn.expired())
        {
            lg(ERROR,"conn is expired");
            return;
        }//这是为了检查Connection对象是否已经被销毁
        std::shared_ptr<Connection> connection = conn.lock();//将weak_ptr转换为shared_ptr
        //由于监听套接字以及连接套接字都是非阻塞的，所以这里需要循环accept，处理有多个连接到来的情况
        while(1)
        {
            int clientfd;
            std::string client_ip;
            uint16_t client_port;
            if(_listen_sock->Accept(clientfd,client_ip,client_port))
            {
                lg(INFO,"Accept a new client: %s:%d",client_ip.c_str(),client_port);
                SetNonBlock(clientfd);
                AddConnection(clientfd,EVENT_IN,
                              std::bind(&TcpServer::Recver,this,std::placeholders::_1),//这里需要绑定this指针，否则会报错
                              std::bind(&TcpServer::Sender,this,std::placeholders::_1),
                              std::bind(&TcpServer::Excepter,this,std::placeholders::_1),
                              client_ip,client_port);

            }
            else
            {
                if(errno==EWOULDBLOCK) break;
                else if(errno==EINTR)//如果是被信号中断，需要重新accept
                    continue;
                else
                {
                    break;
                }
            }
        }
    }

    void Recver(std::weak_ptr<Connection> conn)
    {
        if(conn.expired())
        {
            lg(ERROR,"conn is expired");
            return;
        }
        std::shared_ptr<Connection> connection = conn.lock();
        while(1)
        {
            char buf[1024];
            ssize_t s = recv(connection->SockFd(),buf,sizeof(buf)-1,0);
            if(s>0)
            {
                connection->AppendInbuffer(buf);
            }
            else if(s==0)
            {
                lg(INFO,"client %s:%d close",connection->getIp().c_str(),connection->getPort());
                //_epoller->EpollUpdate(EPOLL_CTL_DEL,conn->SockFd(),0);这里先不删除，可能还要发消息
                connection->_except_cb(connection);
                return;
            }
            else if(s<0)
            {
                if(errno==EWOULDBLOCK) break;
                else if(errno==EINTR) continue;
                else
                {
                    lg(ERROR,"recv error");
                    connection->_except_cb(connection);
                    break;
                }
            }
        }
        _Onmessage(connection);//将接收到的信息交付上层去检查，1.是否完整 2.完整的话就进行处理
    }



    void Sender(std::weak_ptr<Connection> conn)
    {
        if(conn.expired())
        {
            lg(ERROR,"conn is expired");
            return;
        }
        std::shared_ptr<Connection> connection = conn.lock();
        string& outbuffer = connection->Outbuffer();//获取输出缓冲区
        while(true)
        {
            ssize_t n = send(connection->SockFd(), outbuffer.c_str(), outbuffer.size(), 0);
            if(n > 0)
            {
                outbuffer.erase(0, n);//发送成功后，删除已发送的部分
                if(outbuffer.empty()) break;//如果输出缓冲区为空，说明发送完成
            }
            else if(n == 0)
            {
                return;
            }
            else
            {
                if(errno == EWOULDBLOCK) break;
                else if(errno == EINTR) continue;
                else{
                    lg(WARNING, "sockfd: %d, client info %s:%d send error...", connection->SockFd(), connection->getIp().c_str(), connection->getPort());
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        //如果输出缓冲区不为空，说明还有数据没有发送完，需要开启写事件
        if(!outbuffer.empty())
        {
            EnableEvent(connection->SockFd(), true, true);
        }
        else
        {
            //如果输出缓冲区为空，说明发送完成，需要关闭写事件
            EnableEvent(connection->SockFd(), true, false);
        }
    }

    void EnableEvent(int fd,bool enable_read,bool enable_write)
    {
        _epoller->EpollUpdate(EPOLL_CTL_MOD,fd,(enable_read?EVENT_IN:0)|(enable_write?EVENT_OUT:0));//修改事件
    }

    void Excepter(std::weak_ptr<Connection> conn)
    {
        //这个函数是处理异常的，当连接异常时，需要关闭连接
        if(conn.expired())
        {
            lg(ERROR,"conn is expired");
            return;
        }
        std::shared_ptr<Connection> connection = conn.lock();
        int sockfd=connection->SockFd();
        // 1. 移除对特定fd的关心
        _epoller->EpollUpdate(EPOLL_CTL_DEL,sockfd,0);
        // 2. 关闭异常的文件描述符
        close(sockfd);
        // 3. 从unordered_map中移除
        _conn_map.erase(sockfd);

    }

    bool CheckConnection(int fd)
    {
        return _conn_map.find(fd) != _conn_map.end();
    }

    void PrintAllSockets()
    {
        for(auto &it:_conn_map)
        {
            lg(INFO,"sockfd:%d,ip:%s,port:%d",it.second->SockFd(),it.second->getIp().c_str(),it.second->getPort());
        }
    }

    void Dispater(int timeout)
    {
        int n=_epoller->EpollWait(evs,DEFAULT_NUM,timeout);
        if(n<0)
        {
            lg(ERROR,"epoll wait error");
            return;
        }
        //这里来处理事件
        for(int i=0;i<n;i++)
        {
            //如果出现了异常，那么就按照读写事件一起处理
            uint32_t events = evs[i].events;
            int sock = evs[i].data.fd;
            // 统一把事件异常转换成为读写问题
             if (events & EPOLLERR || events & EPOLLHUP)
                 events |= (EPOLLIN | EPOLLOUT);
            // 只需要处理EPOLLIN EPOLLOUT
            if ((events & EPOLLIN) && CheckConnection(sock))
            {
                if (_conn_map[sock]->_recv_cb)
                    _conn_map[sock]->_recv_cb(_conn_map[sock]);
            }
            if ((events & EPOLLOUT) && CheckConnection(sock))
            {
                if (_conn_map[sock]->_send_cb)
                    _conn_map[sock]->_send_cb(_conn_map[sock]);
            }
        }
    }

    void Loop()
    {
        _isrunning =true;
        while(_isrunning)
        {
            Dispater(-1);
            PrintAllSockets();
        }
        _isrunning =false;
    }


    ~TcpServer()
    {
    }
private:
    TcpServer(const TcpServer&) = delete;
    TcpServer& operator=(const TcpServer&) = delete;//反拷贝

    int _port;
    bool _isrunning;
    std::shared_ptr<Sockets> _listen_sock;
    std::shared_ptr<Epoller> _epoller;
    std::unordered_map<int, std::shared_ptr<Connection>> _conn_map;//存储连接
    struct epoll_event evs[DEFAULT_NUM];
    func_t _Onmessage;//将信息交付上层处理
};